package com.ve.searcher.service

import com.ve.searcher.MyConfigurationProperties
import com.ve.searcher.extension.*
import com.ve.searcher.pojo.DocumentDescPojo
import com.ve.searcher.pojo.EverythingFilePojo
import com.ve.searcher.pojo.SearchPojo
import org.apache.lucene.analysis.CharArraySet
import org.apache.lucene.analysis.standard.StandardAnalyzer
import org.apache.lucene.document.Document
import org.apache.lucene.document.Field
import org.apache.lucene.document.StringField
import org.apache.lucene.document.TextField
import org.apache.lucene.index.*
import org.apache.lucene.search.*
import org.apache.lucene.store.FSDirectory
import org.appsugar.archetypes.extension.getLogger
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.attribute.BasicFileAttributes
import java.util.*

@Component
class LuceneServiceImpl : LuceneService {

    @Autowired
    var luncfg: MyConfigurationProperties? = null

    private var indexWriter: IndexWriter? = null


    override fun init() {
        val indexPath = Paths.get(luncfg!!.fileindex)
        if (indexPath.exists()) {
            indexPath.deleteRecursively()
        }
        indexPath.createDirectories()
        Files.createDirectories(indexPath)
        getIndexWriter().commit()
//        getIndexWriter().close()
        luncfg!!.searchpath.forEach {
            val path = Paths.get(it)
            path.walkFileTree(object : AbstractFileVisitor<Path> {
                override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
                    getLogger<LuceneServiceImpl>().warn(file.toAbsolutePath().toString())
                    saveOrUpdate(DocumentDescPojo(file))
                    return FileVisitResult.CONTINUE

                }
            })

        }
    }

    override fun saveOrUpdate(docDesc: DocumentDescPojo) {
        val indexWriter = getIndexWriter()
        val fileName = docDesc.path.toFile().name.toLowerCase()
        //indexWriter.deleteDocuments(Term("filename",fileName))
        val doc = Document()
        doc.add(StringField("filename", fileName, Field.Store.YES))
        doc.add(StringField("filepath", docDesc.path.parent.toString(), Field.Store.YES))
        doc.add(StringField("tag", docDesc.path.parent.fileName.toString(), Field.Store.YES))
        doc.add(TextField("body", docDesc.path.readLines().joinToString(""), Field.Store.YES))
        indexWriter.addDocument(doc)
        indexWriter.commit()
    }

    override fun search(searchPojo: SearchPojo): ArrayList<EverythingFilePojo> {
        val returnObjects = ArrayList<EverythingFilePojo>()
        var bodyStringPQB = PhraseQuery.Builder()
        searchPojo.searchString.trim().split("\\s+".toRegex()).forEach {
            bodyStringPQB.add(Term("body", it))
        }
        bodyStringPQB.setSlop(10)
        val booleanQB1 = BooleanQuery.Builder()
        booleanQB1.add(bodyStringPQB.build(), BooleanClause.Occur.SHOULD)
        if(searchPojo.searchString.contains("\\s+".toRegex())){
            var filenamePQB = PhraseQuery.Builder()
            filenamePQB.add(Term("filename", searchPojo.searchString))
            booleanQB1.add(filenamePQB.build(), BooleanClause.Occur.SHOULD)
        }
        val booleanQB3 = BooleanQuery.Builder()
        booleanQB3.add(booleanQB1.build(), BooleanClause.Occur.MUST)
        val booleanQB2 = BooleanQuery.Builder()
        if (searchPojo.tags == null) {
            booleanQB2.add(PhraseQuery(0, "tag", "NONE"), BooleanClause.Occur.MUST)
        } else {
            searchPojo.tags?.forEach {
                booleanQB2.add(PhraseQuery(0, "tag", it), BooleanClause.Occur.SHOULD)
            }
        }
        booleanQB3.add(booleanQB2.build(), BooleanClause.Occur.MUST)
        val fileset = HashSet<String>()
        for (document in search(booleanQB3.build(), 9999999)) {
            val pojo = EverythingFilePojo()
            val filename = document.get("filename")
            val filepath = document.get("filepath")
            var filepathstr = String.format("%s/%s", filepath, filename)
            if (fileset.contains(filepathstr)) {
                continue
            } else {
                fileset.add(filepathstr)
            }
            pojo.fileName = filename
            pojo.filePath = filepath
            pojo.tag = document.get("tag")
            returnObjects.add(pojo)
        }
        return returnObjects
    }

    private fun search(query: Query, maxHits: Int): ArrayList<Document> {

        // index, index reader, and index searcher
        val indexReader = getIndexReader()
        try {
            val indexSearcher = IndexSearcher(indexReader)
            // get the top docs
            val topDocs = indexSearcher.search(query, maxHits)
            // make the return object
            val documents = ArrayList<Document>()
            // iterate through each document
            for (scoreDoc in topDocs.scoreDocs) {
                // get the document
                val document = indexSearcher.doc(scoreDoc.doc)
                documents.add(document)
            }

            // return the documents with highlight data
            return documents
        } finally {
            indexReader.close()
        }


    }

    private fun getIndexWriter(): IndexWriter {

        if (indexWriter == null || !indexWriter!!.isOpen()) {
            val fsDirectory = FSDirectory.open(Paths.get(luncfg!!.fileindex))
            val indexWriterConfig = IndexWriterConfig(StandardAnalyzer(CharArraySet(luncfg!!.stopword.split(",").toList(), true)))
            indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND)
            indexWriter = IndexWriter(fsDirectory, indexWriterConfig)
        }
        return indexWriter as IndexWriter
    }

    private fun getIndexReader(): IndexReader {
        val fsDirectory = FSDirectory.open(Paths.get(luncfg!!.fileindex))
        return DirectoryReader.open(fsDirectory)
    }

}

